/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: file_storage.cpp
 *
 * Purpose: file storage implementation
 *
 * Developer:
 *   wen.gu , 2019-10-11
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include "panda/per/file_storage.h"

#include <errno.h>
#include <string.h>

#include "panda/sys/property.h"
#include "panda/core/directory.h"
#include "panda/sys/sys_utils.h"

#include "panda/per/internal/file_accessor_impl.h"

#define LOG_TAG "fileS"
#include "panda/core/log.h"

namespace panda
{
namespace per
{
/******************************************************************************
 **    MACROS
 ******************************************************************************/

#define DEFAULT_FILE_STORAGE_DIR "/var/per/"



/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

/** std::string: file name, bool: true: opened, false: not */
using FileInfoMap = std::map<std::string, bool>;


/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
class FileStorage::impl
{
public:
    std::string mStoragePath = "";
    FileInfoMap mFileMap;
};

class FileStorageManager
{
public:
    static FileStorageManager& instance()
    {
        static FileStorageManager fstm;
        return fstm;
    }

    ~FileStorageManager()
    {
        /** todo something */
    }

protected:
    FileStorageManager()
    {
        std::string rootDir = "";

        mFileStorageDir = panda::sys::GetPersistenceDir();
        if (mFileStorageDir.empty())
        {
            mFileStorageDir = DEFAULT_FILE_STORAGE_DIR;
        }
    }

public:


public:
    std::string mFileStorageDir = "";
};


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

//static 
std::shared_ptr<FileStorage> FileStorage::OpenFileStorage(const std::string& fs)
{
    FileStorageManager& fsm = FileStorageManager::instance();
    std::string storagePath = fsm.mFileStorageDir + fs;
    if (panda::core::Directory::IsAccessale(storagePath))
    {
        return  std::shared_ptr<FileStorage>(new FileStorage(storagePath));
    }
   
    return nullptr;
}

FileStorage::FileStorage(const std::string& fs)
    :mImpl(new impl)
{
    mImpl->mStoragePath = fs;

    std::shared_ptr<panda::core::Directory> pDir = panda::core::Directory::OpenDir(fs);

    if (pDir)
    {
        pDir->foreach([](std::string& path, bool isDir, void* opaque)
            {
                if (!isDir) /** todo refine me?? */
                {
                    FileInfoMap& parr = *((FileInfoMap*)opaque);
                    parr[path] = false;
                }

            }, &mImpl->mFileMap);
    }


    /** todo something */
}

FileStorage::~FileStorage()
{
    /** todo something */

}


FileNameArray FileStorage::getAllFileNames()
{
    FileNameArray fma;
    std::shared_ptr<panda::core::Directory> pDir = panda::core::Directory::OpenDir(mImpl->mStoragePath);

    if (pDir)
    {
        pDir->foreach([](std::string& path, bool isDir, void* opaque) 
            {                
                if (!isDir)
                {  
                    FileNameArray& parr = *((FileNameArray*)opaque);
                    parr.push_back(path);
                }
                
            }, &fma);
    }

    return std::move(fma);
}

FileStorage::PandaErrc FileStorage::deleteFile(const std::string& fn)
{
    std::string filePath = mImpl->mStoragePath + fn;
    if (panda::core::Directory::IsAccessale(filePath))
    {
        FileInfoMap::iterator it = mImpl->mFileMap.find(fn);

        if ((it != mImpl->mFileMap.end()) && (!it->second))
        {

            int res = remove(filePath.c_str());
            
            if (res < 0)
            {
                LOGE("delete file(%s) failled(%s)\n", 
                     fn.c_str(), strerror(errno));
                return PandaErrc::Undefined;
            }

            mImpl->mFileMap.erase(it);

            return PandaErrc::OK;
        }
        else
        {
            return PandaErrc::ResourceIsBusy;
        }
    }

    return PandaErrc::NotFound; /** todo refine me?? */

}

bool FileStorage::fileExists(const std::string& fn)
{
    std::string filePath = mImpl->mStoragePath + fn;
    return panda::core::Directory::IsAccessale(filePath);
}

FileStorage::PandaErrc FileStorage::resetFile(const std::string& fn)
{
    std::string filePath = mImpl->mStoragePath + fn;
    if (panda::core::Directory::IsAccessale(filePath))
    {
        FileInfoMap::iterator it = mImpl->mFileMap.find(fn);

        if ((it != mImpl->mFileMap.end()) && (!it->second))
        {
            panda::core::VFS vf;
            PandaErrc ret = vf.fileOpen(filePath, panda::core::VFS::OpenMode::RWT);

            if (bool(ret))
            {
                vf.fileSync();
            }

            return ret;
        }
        else
        {
            return PandaErrc::ResourceIsBusy;
        }
    }

    return PandaErrc::NotFound; /** todo refine me?? */
}

FileStorage::PandaErrc FileStorage::resetAllFiles()
{
    /** todo */
    FileNameArray fna = getAllFileNames();
    if (fna.size() > 0)
    {
        for (size_t i = 0; i < fna.size(); i++)
        {
            PandaErrc ret = resetFile(fna[i]);
            if (ret != panda::core::PandaErrc::OK)
            {
                return ret;
            }
        }
    }

    return PandaErrc::OK; /** todo refine me?? */
}

std::unique_ptr<FileAccessor> FileStorage::openFileReadWrite(const std::string& fn)
{
    std::unique_ptr<internal_impl::FileAccessorImpl> fa(new internal_impl::FileAccessorImpl);

    std::string filePath = mImpl->mStoragePath + fn;

    if (!fa->openFile(filePath, false))
    {
        fa = nullptr;
    }

    return fa;
}

std::unique_ptr<FileAccessor> FileStorage::OpenFileReadOnly(const std::string& fn)
{
    std::unique_ptr<internal_impl::FileAccessorImpl> fa(new internal_impl::FileAccessorImpl);

    std::string filePath = mImpl->mStoragePath + fn;

    if (!fa->openFile(filePath, true))
    {
        fa = nullptr;
    }

    return fa;
}


} /** namespace per */
} /** namespace panda */
